package game
import (
	"math"
	"math/rand"
	"time"
	"strconv"

	"github.com/name5566/leaf/log"
	"game/poker/hand"

	"message"
	"config"
	"github.com/golang/protobuf/proto"
)


// 奖池
type Pot struct {
	value        int
	contributors []int
}

type Room struct {
	roomID                int
	hallID                int
													  // 根据配置初始化的数据
	smallBlind            int                         // 小盲注
	bigBlind              int                         // 大盲注
	ante                  int                         // 前注
	minMoney              int                         // 最小携带金额
	maxMoney              int                         // 携带金额的上限
	hallName              string                      // 大厅名字
	minCount              int                         // 房间最小几人开局
	maxCount              int                         // 房间最大人数
	thinkTime             int                         // 思考时间
	limitBet              int                         // 几倍底池限注，0为不限注
	roomName              string

	players               map[int]*User               // 存放该房间内的玩家
	seats                 [MAX_SEAT_COUNT]bool        // 座位，根据房间最大数目，开放不同的座位，玩家只能坐在开放的座位上
	playersInSeat         [MAX_SEAT_COUNT]*User       // 坐在座位上的玩家

	deck                  *hand.Deck                  // 牌桌，用于发牌
	board                 [MAX_BOARD_COUNT]*hand.Card //公牌
	pots                  []*Pot                      // 奖池
	bets                  map[int]int                 // 玩家的押注，因为玩家可能会离席，但是即便玩家离席，这个钱也会正常归于底池

	round                 message.PokerRound          // 当前回合
	isPlaying             bool                        // 是否已经开始比赛了
	firstBettingPass      bool                        // 是否是第一轮bet，当叫到最后一个人后，如果有加注则会继续bet下去，直到所有bet都相等，即没有人再加注
	lastBetPlayerInRiver  int                         // 在河牌阶段最后一个叫注的人，如果要到摊牌阶段，从这个人开始顺时针亮牌
	lastRaise             int                         // 上个玩家的加注金额（只是额外增加的部分，跟注部分不考虑）

	position              int                         // 当前座位 通过座位来标注玩家，尽量不记录中间玩家变量，防止玩家中途离开房间的情况
	currentThinkStartTime int64                         // 当前玩家的思考开始的时间，用于其他玩家进入房间的时候同步倒计时
	smallBlindSeat        int                         // 小盲注的座位
	bigBlindSeat          int                         // 大盲注的座位
	dealerSeat            int                         // 庄家座位
	lastToTalkSeat        int                         // 记录最后一个行动的玩家座位 必须要都行动一轮才能判定回合结束
}

func (self *Room) str() string {
	return strconv.Itoa(self.roomID)
}

func (self *Room) onInit() {
	cfg := config.GetHallConfig(self.hallID)
	if cfg == nil {
		log.Error("无法找到大厅配置: %v", self.hallID)
		return
	}

	self.players = make(map[int]*User)
	self.bets = make(map[int]int)
	self.smallBlind = cfg.SmallBlind
	self.bigBlind = cfg.BigBlind
	self.ante = cfg.Ante
	self.minMoney = cfg.MinMoney
	self.maxMoney = cfg.MaxMoney
	self.minCount = cfg.MinCount
	self.maxCount = cfg.MaxCount
	self.hallName = cfg.HallName
	self.thinkTime = cfg.ThinkTime
	self.limitBet = cfg.LimitBet
	self.roomName = cfg.HallName + strconv.Itoa(self.roomID)

	self.deck = hand.NewDealer().Deck() // 牌桌，用于发牌
	self.round = message.PokerRound_ROUND_NONE
	self.isPlaying = false
	self.firstBettingPass = true
	self.lastBetPlayerInRiver = 0
	self.lastRaise = 0
	self.position = -1
	self.currentThinkStartTime = 0
	self.smallBlindSeat = -1
	self.bigBlindSeat = -1
	self.dealerSeat = -1
	self.lastToTalkSeat = -1

	self.initSeats()
}

func (self *Room) initSeats() {
	var seatCfg = make(map[int][]int)
	seatCfg[2] = []int{2, 7}
	seatCfg[3] = []int{2, 7, 5}
	seatCfg[4] = []int{1, 6, 3, 8}
	seatCfg[5] = []int{0, 2, 4, 6, 8}
	seatCfg[6] = []int{0, 2, 4, 5, 7, 8}
	seatCfg[7] = []int{0, 2, 3, 4, 5, 6, 8}
	seatCfg[8] = []int{1, 2, 3, 4, 5, 6, 7, 8}
	seatCfg[9] = []int{0, 1, 2, 3, 4, 5, 6, 7, 8}

	seatOpened := seatCfg[self.maxCount]
	for i := 0; i < MAX_SEAT_COUNT; i++ {
		find := false
		for _, v := range seatOpened {
			if v == i {
				self.seats[i] = true
				find = true
				break
			}
		}

		if !find {
			self.seats[i] = false
		}
	}
}

// 获得当前牌局数据，用于新玩家进入或者是特殊情况下的数据同步
func (self *Room) getInfo() *message.RoomInfo {
	var thinkStartTime int64 = 0
	if self.currentThinkStartTime > 0 {
		thinkStartTime = time.Now().Unix() - self.currentThinkStartTime
	} else {
		thinkStartTime = 0
	}

	var playerInfo []*message.GamePlayerInfo
	for _, v := range self.playersInSeat {
		if v != nil {
			playerInfo = append(playerInfo, v.getInfo())
		}
	}

	var pots []int32
	for _, v := range self.pots {
		pots = append(pots, int32(v.value))
	}

	var boards []int32
	for _, v := range self.board {
		if v != nil {
			boards = append(boards, int32(v.Index()))
		}
	}

	var seats []bool
	for _,v := range self.seats {
		seats = append(seats, v)
	}

	return &message.RoomInfo{
		RoomID:proto.Int(self.roomID),
		RoomName:proto.String(self.roomName),
		SmallBlind:proto.Int(self.smallBlind),
		BigBlind:proto.Int(self.bigBlind),
		Ante:proto.Int(self.ante),
		LimitBet:proto.Int(self.limitBet),
		MinMoney:proto.Int(self.minMoney),
		MaxMoney:proto.Int(self.maxMoney),
		MinCount:proto.Int(self.minCount),
		MaxCount:proto.Int(self.maxCount),
		Seats:seats,
		BigBlindSeat:proto.Int(self.bigBlindSeat),
		SmallBlindSeat:proto.Int(self.smallBlindSeat),
		DealerSeat:proto.Int(self.dealerSeat),
		Position:proto.Int(self.position),
		ThinkTime:proto.Int(self.thinkTime),
		ThinkElapseTime:proto.Int(int(thinkStartTime)),
		Round:self.round.Enum(),
		Board:boards,
		Players:playerInfo,
		Pots:pots,
	}
}

// 重置房间
func (self *Room) reset() {
	self.round = message.PokerRound_ROUND_NONE
	self.board = [MAX_BOARD_COUNT]*hand.Card{}
	self.pots = []*Pot{&Pot{value:0}}    // 默认底池
	self.bets = make(map[int]int)
	self.isPlaying = false
}

// 是否可以加入（判定人数）
func (self *Room) couldEnter() bool {
	return self.getPlayerInSeatCount() < self.maxCount
}

// 判断此座位是否可以入座
func (self *Room) isSeatValid(seatIndex int) bool {
	// 如果此座位开放，并且没有坐上玩家，则可以入座
	if !self.seats[seatIndex] {
		return false
	}

	if self.playersInSeat[seatIndex] != nil {
		return false
	}
	return true
}

// 寻找合适的座位，-1为没有找到 座位不一定全部开放
func (self *Room) getValidSeat() int {
	for i := 0; i < MAX_SEAT_COUNT; i++ {
		if self.isSeatValid(i) {
			return i
		}
	}
	return -1
}

// 随机取一个玩家
func (self *Room) getRandomPlayer() *User {
	ret := self.playersPlaying()
	index := rand.Intn(len(ret))
	return ret[index]
}

// 根据玩家id，获取玩家对象
func (self *Room) getPlayer(playerID int) *User {
	if v, ok := self.players[playerID]; ok {
		return v
	} else {
		return nil
	}
}

// 根据当前的座位，获取下一个玩家
func (self *Room) getNextPlayer(seat int, pred func(*User) bool) *User {
	// 从下一个位置开始找，如果没有找到，则从头开始找
	for i := seat + 1; i < MAX_SEAT_COUNT; i ++ {
		player := self.playersInSeat[i]
		if player == nil || player.state == message.PlayerState_STATE_STAND || player.state == message.PlayerState_STATE_READY {
			continue
		}
		if pred == nil || pred(player) {
			return player
		}
	}

	// 从头找
	for i := 0; i < seat; i++ {
		player := self.playersInSeat[i]
		if player == nil || player.state == message.PlayerState_STATE_STAND || player.state == message.PlayerState_STATE_READY {
			continue
		}
		if pred == nil || pred(player) {
			return player
		}
	}

	return nil
}

// 获取下一个座位
func (self *Room) getNextPosition(seat int, pred func(*User) bool) int {
	player := self.getNextPlayer(seat, pred)
	if player != nil {
		return player.seat
	}

	return -1
}

// 根据当前的座位，获取上一个玩家
func (self *Room) getPrevPlayer(seat int, pred func(*User) bool) *User {
	for i := seat - 1; i > 0; i-- {
		player := self.playersInSeat[i]
		if player == nil || player.state == message.PlayerState_STATE_STAND || player.state == message.PlayerState_STATE_READY {
			continue
		}
		if pred == nil || pred(player) {
			return player
		}
	}

	// 从头找
	for i := MAX_SEAT_COUNT - 1; i > seat; i-- {
		player := self.playersInSeat[i]
		if player == nil || player.state == message.PlayerState_STATE_STAND || player.state == message.PlayerState_STATE_READY {
			continue
		}
		if pred == nil || pred(player) {
			return player
		}
	}
	return nil
}

// 返回上一个座位
func (self *Room) getPrevPosition(seat int, pred func(*User) bool) int {
	player := self.getPrevPlayer(seat, pred)
	if player != nil {
		return player.seat
	}
	return -1
}

// 玩家进入房间
func (self *Room) onPlayerEnterRoom(player *User, autoSitDown bool, autoStart bool) {
	log.Debug("onPlayerEnterRoom:  %v", player.str())
	self.players[player.ID()] = player
	player.roomID = self.roomID
	player.room = self
	player.money = int(math.Min(float64(player.data.TotalMoney), float64(self.maxMoney)))    // 玩家携带的筹码
	player.data.TotalMoney = player.data.TotalMoney - int64(player.money) // 减去玩家总金钱

	// 玩家进入房间先设置为stand状态，如果坐下，则修改为ready状态，如果开始游戏，则设置为playing状态
	player.state = message.PlayerState_STATE_STAND

	// 通知玩家进入房间
	player.SendMsg(message.MSG_ENTER_ROOM, &message.S2C_Room{RoomInfo:self.getInfo()})

	// 玩家已经坐满了，不能坐下，但是可以正常进入房间 (其他玩家是不会看到没有坐下的玩家的)
	if self.getPlayerInSeatCount() >= self.maxCount {
		return
	}

	if !autoSitDown {
		return
	}

	seatIndex := self.getValidSeat()
	if seatIndex != -1 {
		// 有合适的座位，直接加入游戏
		self.onPlayerSitDown(player, seatIndex)
	} else {
		log.Error("无法找到合适的座位： %v", seatIndex)
	}

	// 如果还没有开始比赛，并且满足最小开场人数，则直接开始比赛
	if autoStart && !self.isPlaying && self.getPlayerInSeatCount() >= self.minCount {
		self.beginTurn()
	}

	log.Debug("玩家 %v 坐在了座位上: %v", player.str(), seatIndex)
}

// 玩家离开房间
func (self *Room) onPlayerLeaveRoom(player *User) {
	log.Debug("onPlayerLeaveRoom:  %v", player.str())

	// 如果玩家正在牌桌上，则先处理玩家站起的逻辑
	self.onPlayerStandUp(player)

	// 清空玩家的房间数据
	if _, ok := self.players[player.ID()]; ok {
		delete(self.players, player.ID())
	}

	player.roomID = 0
	player.room = nil
	player.data.TotalMoney += int64(player.money) // 兑换筹码
	if !player.bot {
		self.checkCleanRoom()
	}
}

// 玩家选择座位开始游戏
func (self *Room) onPlayerSitDown(player *User, pos int) {
	log.Debug("onPlayerSitDown:  %v  %v", player.str(), pos)

	// 玩家已经在座位上了 但是位置并不相同
	if pos > -1 && player.seat >= 0 && player.seat != pos {
		log.Error("玩家已经坐在其他的位置上: %v", player.seat)
		return
	}

	// 玩家没钱，不能坐下
	if player.money <= self.minMoney {
		log.Error("玩家金钱不足: %v", player.money)
		return
	}

	player.seat = pos

	// 新加入的玩家状态设置为ready，不能参与已经进行的游戏，但是会自动开始加入下一局
	player.state = message.PlayerState_STATE_READY
	self.playersInSeat[pos] = player
	self.broadcastSitDown(player)
}

// 玩家站起，广播给其他玩家，该玩家离开房间之前，所有数据也会广播给他
func (self *Room) onPlayerStandUp(player *User) {
	log.Debug("onPlayerStandUp:  %v", player.str())
	// 先记录一个玩家的座位，因为后面会重置它
	seat := player.seat

	// 如果玩家正在比赛中，则执行弃牌操作，即便玩家已经all-in，玩家也会弃牌
	// 离席的玩家的押注依然在牌桌上，当此回合结束的时候统一归于奖池
	if player.state == message.PlayerState_STATE_PLAYING || player.state == message.PlayerState_STATE_ALL_IN {
		log.Debug("onPlayerStandUp:  %v  fold", player.str())
		self.actionForceFold(player)
	}

	if self.removePlayerInSeat(player) {
		self.broadcastStandUp(seat)
	}

	if self.getPlayerInGameCount() < 2 && self.round != message.PokerRound_ROUND_NONE {
		// 没有足够的人数继续比赛了，直接判定当前存在的玩家胜利
		self.addBetsToPot()

		// 通知客户端奖池数据改变
		self.broadcastPotChanged()
		self.processPostRiver()
	}
}

// 踢除所有玩家
func (self *Room) onKickAllPlayer() {
	log.Debug("onKickAllPlayer")
	var temp []*User
	for _, player := range self.players {
		temp = append(temp, player)
	}
	for _, player := range temp {
		player.killPlayer()
	}
}

// 踢除所有的机器人
func (self *Room) onKillBots() {
	log.Debug("onKillBots")
	var temp []*User
	for _, player := range self.players {
		temp = append(temp, player)
	}
	for _, player := range temp {
		if player.bot {
			// 移除玩家
			if self.playersInSeat[player.seat] != nil {
				self.playersInSeat[player.seat] = nil
			}
			delete(self.players, player.ID())

			player.roomID = 0
			player.room = nil

			// 删除玩家对象
			player.killPlayer()
		}
	}
}

// 玩家选择执行一个操作(由客户端发起请求，服务器做校验，然后执行对应逻辑并广播给所有客户端)
func (self *Room) onPlayerAction(player *User, action message.PokerAction, param int) {
	log.Debug("onPlayerAction:  %v  action:%v  param:%v", player.str(), action, param)

	switch action {
	case message.PokerAction_ACTION_FOLD:
		self.actionFold(player)
	case message.PokerAction_ACTION_CHECK:
		self.actionCheck(player)
	case message.PokerAction_ACTION_CALL:
		self.actionCall(player)
	case message.PokerAction_ACTION_RAISE:
		self.actionRaise(player, param)
	case message.PokerAction_ACTION_ALL_IN:
		self.actionAllIn(player)
	}
}

// 玩家选择亮牌
func (self *Room) onPlayerShowCard(player *User) {
	log.Debug("onPlayerShowCard: %v  %v %v", player, player.handCard1, player.handCard2)
	if player == nil || player.state == message.PlayerState_STATE_STAND {
		log.Error("玩家没有在比赛中")
	}
	self.broadcastShowCard(player)
}

// 删除玩家
func (self *Room) removePlayerInSeat(player *User) bool {
	for i := 0; i < MAX_SEAT_COUNT; i++ {
		p := self.playersInSeat[i]
		if p != nil && p == player {
			player.seat = -1
			self.playersInSeat[i] = nil
			return true
		}
	}
	return false
}

// 根据座位获取玩家对象
func (self *Room) getPlayerBySeat(seat int) *User {
	return self.playersInSeat[seat]
}

// 检查是否需要清理房间（当玩家都走了，只剩下机器人的时候）
func (self *Room) checkCleanRoom() {
	log.Debug("checkCleanRoom")
	for _, v := range self.players {
		if v != nil && !v.bot {
			return
		}
	}
	log.Debug("DoCleanupRoom")
	// 只剩下机器人了，清理
	self.onKillBots()

	// 没有足够的人的情况下，清理房间
	if self.getPlayerInSeatCount() < 2 {
		self.reset()
	}
}

// 当前座位上的玩家
func (self *Room) getCurrentPlayer() *User {
	return self.getPlayerBySeat(self.position)
}

// 获取做在座位上的玩家数目(无论玩家有没有开始进行游戏)
func (self *Room) getPlayerInSeatCount() int {
	return len(self.playersAll())
}

// 获取正在游戏的玩家数目(如果这个数目小于2，则游戏直接结束)
func (self *Room) getPlayerInGameCount() int {
	return len(self.playersInGame())
}

// 在座位上的所有玩家（也包括没有开始比赛的）
func (self *Room) playersAll() []*User {
	var players []*User
	for _, player := range self.playersInSeat {
		if player != nil && player.state != message.PlayerState_STATE_STAND {
			players = append(players, player)
		}
	}
	return players
}

// 所有在座位上的玩家（包括全下和弃牌的玩家，但是不包括没有参与比赛的玩家）
func (self *Room) playersInGame() []*User {
	var players []*User
	for _, player := range self.playersInSeat {
		if player != nil && (player.state == message.PlayerState_STATE_PLAYING || player.state == message.PlayerState_STATE_ALL_IN || player.state == message.PlayerState_STATE_FOLD) {
			players = append(players, player)
		}
	}
	return players
}

// 所有正在比赛中的玩家(不包括全下和弃牌的)，即，能行动的玩家
func (self *Room) playersPlaying() []*User {
	var players []*User
	for _, player := range self.playersInSeat {
		if player != nil && player.state == message.PlayerState_STATE_PLAYING {
			players = append(players, player)
		}
	}
	return players
}

// 没有弃牌的玩家（包含all-in的，这些玩家参与最终的比牌）
func (self *Room) playersNotFold() []*User {
	var players []*User
	for _, player := range self.playersInSeat {
		if player != nil && (player.state == message.PlayerState_STATE_PLAYING || player.state == message.PlayerState_STATE_ALL_IN) {
			players = append(players, player)
		}
	}
	return players
}

